//
//  server.c
//  reli_udp
//
//  Created by Eric Sun on 11/20/12.
//  Copyright (c) 2012 Eric Sun. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "packet.h"
#include "para.h"

static int cwnd;

void compute_timer(double t1, double t2, struct timeval * tv) {
	double diff, frac;
	diff = t2 - t1;
	frac = diff - ((long)diff);
	// no overflow
	if (1000000.0 - tv->tv_usec > frac) {
		tv->tv_sec += diff;
		tv->tv_usec += frac;
	}
	// overflow
	else {
		tv->tv_sec += (diff + 1);
		tv->tv_usec = abs(tv->tv_usec - frac);
	}
}

// initialize the window
void init_window(struct Window * window[]) {
	int i;
	for (i = 0; i < cwnd; i++) {
		window[i] = malloc(sizeof(struct Window));
		bzero(window[i], sizeof(struct Window));
		// not needed after bzero
		window[i]->used = 0;
	}
}

// remove a packet from window if it's at the first position and is received
// by the receiver
void pop(struct Window * window[], unsigned int * ack_expect) {
	//if (window[0]->packet.seq == (ack - window[0]->packet.len)) {
		printf("Packet %u removed...\n", window[0]->packet.seq);
		int i;
		for (i = 0; i < (cwnd-1); i++)
			memcpy(window[i], window[i+1], sizeof(struct Window));
		bzero(window[i], sizeof(struct Window));
		// not needed after bzero
		window[i]->used = 0;
	//}
}

// put a new packet to the window, also put in packet sent time
void push(struct Window * window[], struct Packet * new_packet, struct timeval * tv) {
	int i = empty(window);
	memcpy(&(window[i]->packet), new_packet, PACKET_SIZE);
	memcpy(&(window[i]->tv), tv, sizeof(struct timeval));
	window[i]->used = 1;
}

// return the slot number if detect any empty slot, otherwise return -1
int empty(struct Window * window[]) {
	int i;
	for (i = 0; i < cwnd; i++) {
		if (window[i]->used == 0) {
			return i;
		}
	}
	return -1;
}

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in sender_addr, receiver_addr;
    int sender_port, receiver_len;
    double pl, pc;
    struct Packet *packet_s, *packet_r;
    FILE *fd;
	unsigned int seq;
	unsigned int ack_expect;
	char set_ack;
    
	/* argument debug
     for(i = 0; i < argc; i++ )
     {
     printf( "arg %d: %s\n", i, argv[i] );
     }
     */
    
	// get port number from argument or use the default one
	if (argc < 2) {
        sender_port = SENDER_PORT;
        cwnd = (int)(CWND/PACKET_SIZE);
        pl = PL;
        pc = PC;
    }
	else if (argc == 5) {
		sender_port = atoi(argv[1]);
        cwnd = (int)(atoi(argv[2])/PACKET_SIZE);
        pl = strtod(argv[3], NULL);
        pc = strtod(argv[4], NULL);
    }
	else
	{
		perror("Usage: server [port CWnd Pl Pc]");
		exit(1);
	}

	// create the window with size cwnd to contain n packets
	struct Window * window[cwnd];
	init_window(window);

	// create socket
	if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket error");
		exit(1);
	}
	bzero(&sender_addr, sizeof(sender_addr));

	bzero(&receiver_addr, sizeof(receiver_addr));
	sender_addr.sin_family = AF_INET;
	sender_addr.sin_addr.s_addr = INADDR_ANY;
	sender_addr.sin_port = htons(sender_port);
    
	// bind port
	if (bind(sockfd, (struct sockaddr *)&sender_addr, sizeof(sender_addr)) == -1)
	{
		perror("bind error");
		exit(1);
	}

	packet_r = malloc(PACKET_SIZE);
	packet_s = malloc(PACKET_SIZE);
	char recv_buffer[BUFFER_SIZE];
    char send_buffer[BUFFER_SIZE];
	char read_buffer[PAYLOAD];
    
	/*
	* receive requests from client
	*/
	printf("%s\n", "Receiving request...");
	if (recvfrom(sockfd, recv_buffer, BUFFER_SIZE, 0,
				 (struct sockaddr *) &receiver_addr, &receiver_len) == -1) {
		perror("receive error");
		exit(1);
	}
	else
	{
		memcpy(packet_r, recv_buffer, BUFFER_SIZE);
		if (packet_r->flagReq == 1) {
			printf("Request message received. Request file: %s\n", packet_r->data);
		}
		// need to send ack to receiver, or shall we put ack in the first
		// data packet??
		fd = fopen(packet_r->data, "rb");
		if (fd == NULL) {
			printf("Error when opening file: No such file\n");
		}
		else {
			printf("File opened\n");
		}
	}

	/*
	* read and send file
	*/
	fd_set fds;
	struct timeval tv, now;
	reset_select(&sockfd, &fds, &tv);
	int retval;
	seq = 0;
	ack_expect = 0;
	set_ack = 1;
	while (1) {
		// if not end of file and there's still space, send as many packets as 
		// possible
		if (!feof(fd) && (empty(window) != -1)) {
			int i;
			for (i = empty(window); i<cwnd; i++) {
				if (feof(fd))
					break;
				printf("Empty position available in window, send new packet\n");
				// read and send one packet
				size_t result = fread(&read_buffer, 1, PAYLOAD, fd);
			    makeSeq(packet_s, seq, &read_buffer, result);
			    // setup ack number and next packet's seq
				if (set_ack == 1) {
					ack_expect = seq + result;
					set_ack = 0;
				}
			    seq += result;
			    if (feof(fd))
			    	packet_s->flagEnd = 1;
			    sendPacket(sockfd, &receiver_addr, packet_s);
			    // get packet sent time
			    gettimeofday(&now, NULL);
			    printf("Packet %u sent\n", packet_s->seq);
			    push(window, packet_s, &now);
			}
		}

		// assert the timer
		retval = select(sockfd+1, &fds, NULL, NULL, &tv);
		if (retval == -1) {
			perror("select error");
		}
		// if receive any ack during timeout, handle it
		else if (retval) {
			// FD_ISSET(sockfd, &fds)
			recvfrom(sockfd, recv_buffer, BUFFER_SIZE, 0,
				 (struct sockaddr *) &receiver_addr, &receiver_len);
			memcpy(packet_r, recv_buffer, BUFFER_SIZE);
		if (feof(fd) && (empty(window) == 1) &&
			(packet_r->ack == (window[0]->packet.seq + window[0]->packet.len))) {
			printf("Ack packet received. Ack number: %u\n", packet_r->ack);
			printf("Finish sending packets. Program exits\n");
			break;
		}
			// if ack was supposed to be lost or corrupted, ignore the ack
			if (lost(pl)) {
				printf("Ack packet %u lost...\n", packet_r->ack);
				continue;
			}
			if (corrupted(pc)) {
				printf("Ack packet %u corrupted...\n", packet_r->ack);
				continue;
			}
			// if not lost/corrupted
			if (packet_r->flagAck) {
				// if it's a correct ack, remove all possible packets from window.
				if (packet_r->ack >= ack_expect) {
					double t1, t2;
					// copy down the first packet's timestamp for computing timer
					t1 = window[0]->tv.tv_sec+(window[0]->tv.tv_usec/1000000.0);
					// pop out all received packets
					do {
						printf("Ack packet received. Ack number: %u\n", packet_r->ack);
						pop(window, &ack_expect);
						// if we removed everything, breakout
						if (empty(window) == 0)
							break;
					} while(window[0]->packet.seq < packet_r->ack);

					// now we can compute the timer
					t2 = window[0]->tv.tv_sec+(window[0]->tv.tv_usec/1000000.0);
					// if the whole window is received, we should reset select
					// and ack_expect;
					if (empty(window) == -1) {
						set_ack = 1;
						reset_select(&sockfd, &fds, &tv);
					}
					// otherwise call compute_timer for computing
					else {
						ack_expect = window[0]->packet.seq + window[0]->packet.len;
						compute_timer(t1, t2, &tv);
					}
				}
				// if it's not a correct ack, ignore it.
				else
					continue;
			}
			// we never should reach here
			else
				printf("Non ack packet received. Error\n");
		}
		// timeout, retransmit all packets in window
		else {
			printf("Send previous packet, SEQ number: ");
			int i;
			for (i = 0; i < cwnd; i++) {
				if (window[i]->used == 0)
					break;
				printf("%u ", window[i]->packet.seq);
				sendPacket(sockfd, &receiver_addr, window[i]);
				// reset all the packet sent time
				gettimeofday(&now, NULL);
				memcpy(&(window[i]->tv), &now, sizeof(struct timeval));
			}
			printf("\n");
			// after retransmission, reset select
			reset_select(&sockfd, &fds, &tv);
		}
	}
	fclose(fd);
	close(sockfd);
}